package ch.epfl.lara.matcheck

import ch.epfl.lara.matcheck.types._
import ch.epfl.lara.matcheck.ast.Formula

import scala.collection.mutable.{Map,HashMap}
import scala.collection._

  sealed abstract class Symbol {
    def getName : String
  }

  sealed abstract class TypeSymbol extends Symbol {
    protected var methods: Map[String,MethodSymbol] = HashMap.empty[String,MethodSymbol]
 
    def enterMethod(m: MethodSymbol): Unit = {
      methods + ((m.getName,m))
    }
  

    def lookupMethod(name: String): Option[MethodSymbol] = 
     methods.get(name) 
 
    def allMethods: List[MethodSymbol] =
      methods.values.toList;
  }

  final case class ClassSymbol(isAbs: Boolean, name: String,
                               var invariant: Option[Formula],
                               superclass: Option[ClassSymbol]) extends TypeSymbol {
  
    val superclasses: List[ClassSymbol] =
      this :: (superclass match {
        case None => Nil
        case Some(c) => c.superclasses
      });
  
    //private var fields: Map[String,FieldSymbol] = HashMap.empty[String,FieldSymbol]
    private var fields: immutable.Map[String,FieldSymbol] = new immutable.ListMap[String,FieldSymbol].empty[FieldSymbol]
      /*superclass match {
       case None => HashMap.empty[String,FieldSymbol]
         case Some(s) => s.fields
      }*/

  
  
    superclass match {
      case None => HashMap.empty[String,MethodSymbol]
      case Some(s) => methods = s.methods
    }
  
  

  
    def isSubclassOf(that: ClassSymbol): Boolean =
      superclasses.contains(that);
  
    def lookupField(name: String): Option[FieldSymbol] = fields.get(name)
  
    def enterField(field: FieldSymbol) = 
      fields = fields + ((field.getName,field))
  
  

    def allFields: List[FieldSymbol] =
      fields.values.toList;

 
    override def getName : String = name
    override def toString : String = "class" + name;

  }


  final case class ObjectSymbol(name: String) extends TypeSymbol  {
  
    override def getName : String = name
    override def toString : String = "object"+name
  }

  final case class MethodSymbol(name: Name[TypeSymbol], params: List[(String,Type)], resTpe: Type, var ppc: Option[(Formula,Formula)]) extends Symbol {
    val RES: String = "res"
    override def getName : String = name.getName
    def getContainerName: String = getSym.getName
    def getSym: Symbol = name.getSym
  }



  final case class FieldSymbol(name: Name[ClassSymbol], fieldTpe: Type) extends Symbol {
    override def getName : String = name.getName
  }

  abstract class VarSymbol(name: String, varTpe: Type) extends Symbol {
    override def getName : String = name
    def getTpe : Type = varTpe
  } 

  final case class ParamSymbol(name: Name[MethodSymbol], tpe: Type) extends VarSymbol(name.getName,tpe) 

  final case class LocalSymbol(name: String, tpe: Type) extends VarSymbol(name,tpe) 

